Calling WCF Services from a Linux C++ Client Using gSOAP

Before I get into part II of my earlier post about Configuring and Debugging WCF Services, I want to write about a problem I solved recently. Took a lot of digging around and so I thought I would save someone time by making this post.

Disclaimer: I am primarily a Windows Developer. I usually don’t have any issues going between platforms, languages and such but by no means am I an expert on the Linux platform (or on SOAP for that matter).

The Motivation

The basic problem I was trying to solve was trying to hit a WCF service from Linux using C++. Why was I doing that you might ask. Well, in most environments out there today, programmers and architects have to make heterogeneous systems work together and deal with  legacy systems that will not die (the reasons are many, and complicated).

Service Oriented Architectures come with the promise of making this integration between different platforms easy to achieve, maintain and evolve. However, choosing the technologies to implement SoA in is an important decision, as to be useful, the services you create should be accessible from every platform easily. Only then will you get the buy in needed for SoA to succeed.

gSOAP

gSOAP is a technology that allows you to create stubs for client and server side code from WSDLs. There is a lot more that gSOAP can do but I only used it for creating the client side stubs using the WSDL I got from my WCF service.

You can read all about it here.

Making it Work

OK, let’s get down to how we can use gSOAP to access a WCF service using a C++ client. The steps I had to take were

  1. Install the gSOAP library on my Linux box, in my local directory
  2. Generate the SOAP stubs for the target WCF service
  3. Create the client

Here are the steps again with many more details:

Install the gSOAP library

1. Download the gSOAP tar file. The website is here.

wget http://sourceforge.net/projects/gsoap2/files/gSOAP/2.7.14%20stable%20%28update%29/gsoap_2.7.14.tar.gz/download

2. Untar the file.

tar -xvzf packagename.tar.gz

3. Make and install. Run the following commands.

./configure

make

// The exec_prefix is to install in your home directory
make install exec_prefix=$HOME

Generate the SOAP stubs for the target service.

I installed gSOAP in my home directory. Also lets assume that the service I am trying to connect to exposed an endpoint with BasicHttpBinding, which boils down to simple SOAP. And it is located at the following address

http://www.myserver.com/myWCFservice.svc

and the method I am trying to invoke has the following signature:

// The service implements the IMyService interface 
// and the interface has the GetCount method on it
int IMySevice.GetCount(string inputString)

You would need to execute the following commands to get your SOAP stubs:

Go to the appropriate directory for the platform you are on.

cd /lib/gSOAP/gsoap-2.7/gsoap/bin/linux386

Generate the WSDL header file.

./wsdl2h -o mywcfheader.h http://myserver.com/myWCFService.svc?wsdl

Generate the stub files by executing the gSOAP compiler

./soapcpp2 -I "/lib/gSOAP/gsoap-2.7/gsoap/" mywcfheader.h

At this point you should have the stub files in you directory. As far as the client is concerned, you care about these files

File Name

Description

BasicHttpBinding_IMyService.GetCount.req.xml Example SOAP Request
BasicHttpBinding_IMyService.GetCount..res.xml Example SOAP Response
soapBasicHttpBinding_IMyServiceProxy.h C++ Proxy that wraps the SOAP calls that are made on the client side into an Object Oriented interface. The client you write will consume this.
BasicHttpBinding_IMyService.nsmap File that defines the name space (for versioning of the SOAP protocol) of various schema prefixes
soapC.cpp, soapClient.cpp, soapClientLib.cpp, soapH.h and soapStub.h Client Side SOAP Code that the C++ proxy consumes

Create the client.

The client code is pretty simple. It looks something like this:

// This will be the name of the proxy file created when
// you generate the stubs
#include “soapBasicHttpBinding_IMyServiceProxy.h”

// This will be the nsmap file created when you created
// the stubs
#include “BasicHttpBinding_IMyService.nsmap”

using namespace std;

int main()

{
    // This will be the name of the service
    // class in the proxy header file from above
    BasicHttpBinding_IMyService s;

    // This is the request and response that
    // the service you are trying to call takes.
    // Again you can find the types in the class
    // used in the C++ proxy header
    _ns1__GetCount req;
    _ns1__GetCountResponse resp;

    string is(”Hello There America”);
    req.inputString = &is;

    int err = s.__ns1__GetCount(&req, &resp);

    if (SOAP_OK == err)
        cout << “Service Returned: ” << *resp.GetCountResult << endl;
    else
        cout << “Error: ” << err << endl;

    return 0;
}
2: // This will be the name of the proxy file created when
3: // you generate the stubs
4: #include “soapBasicHttpBinding_IMyServiceProxy.h”
5:
6: // This will be the nsmap file created when you created
7: // the stubs
8: #include “BasicHttpBinding_IMyService.nsmap”
9:
10: using namespace std;
11:
12: int main()
13: {
14:         // This will be the name of the service
15:         // class in the proxy header file from above
16:         BasicHttpBinding_IMyService s;
17:
18:         // This is the request and response that
19:         // the service you are trying to call takes.
20:         // Again you can find the types in the class
21:         // used in the C++ proxy header
22:         _ns1__GetCount req;
23:         _ns1__GetCountResponse resp;
24:
25:         string is(“Hello There America”);
26:         req.inputString = &is;
27:
28:         int err = s.__ns1__GetCount(&req, &resp);
29:
30:         if (SOAP_OK == err)
31:                 cout << “Service Returned: ” <<
32:                     *resp.GetCountResult << endl;
33:         else
34:                 cout << “Error: ” << err << endl;
35:
36:         return 0;
37: }
38:

Since our binding is BasicHttpBinding, the WCF service expects SOAP 1.1 as the protocol. By default gSOAP 2.7 talks in SOAP 1.2. So we need to make sure that we make changes required to generate a client that will communicate using SOAP 1.1.

We can do this by changing the following in your *.nsmap and C++ Proxy header file.

Change:

{“SOAP-ENV”, “http://www.w3.org/2003/05/soap-envelope”, “http://www.w3.org/2003/05/soap-envelope”, NULL},
{“SOAP-ENC”, “http://www.w3.org/2003/05/soap-encoding”, “http://www.w3.org/2003/05/soap-encoding”, NULL},
{“xsi”, “http://www.w3.org/2001/XMLSchema-instance”, “http://www.w3.org/*/XMLSchema-instance”, NULL},
{“xsd”, “http://www.w3.org/2001/XMLSchema”, “http://www.w3.org/*/XMLSchema”, NULL}

to

{“SOAP-ENV”, “http://schemas.xmlsoap.org/soap/envelope/”, NULL, NULL},
{“SOAP-ENC”, “http://schemas.xmlsoap.org/soap/encoding/”, NULL, NULL},
{“xsi”, “http://www.w3.org/2001/XMLSchema-instance”, NULL, NULL},
{“xsd”, “http://www.w3.org/2001/XMLSchema”, NULL, NULL}

I found this information here.

Compile the client

Now we are ready to compile the client. Besides the client code you write and the stub files generated by gSOAP, we also need to supply the compiler with another file stdsoap2.cpp that comes with the installation. Also we need to add the gSOAP lib path to the include path so that it can pick up the appropriate libraries it needs.

The command will look like this:

g++ -I "/lib/gSOAP/gsoap-2.7/gsoap" myclient.cpp soapC.cpp soapClient.cpp /lib/gSOAP/gsoap-2.7/gsoap/stdsoap2.cpp

At this point things should work. Leave comments if something does not work for you or if there are other ways to do this.

About Jaspreet Bakshi

Co-founder and CTO at DotAlign, Inc. Designing and building amazing products at DotAlign.
This entry was posted in web services and tagged , , . Bookmark the permalink.

11 Responses to Calling WCF Services from a Linux C++ Client Using gSOAP

  1. noname00 says:

    Ahoy, this is some useful information.
    Thanks a lot and keep up the good work!

  2. Danette says:

    When I run wsdl2h : (wsdl2h -i -s -o myfile.h http://urltothe:port/?wsdl) it runs fine and then I run soapcpp2 -i -C myfile.h I do not get a soapClient.cpp nor the soapClientLib.cpp, but do get the soapC.cpp, stdsoap2.h proxy.h the nsmap and the other files, but no soapClient.cpp or soapClientLib.cpp. Is there a bug in 2.7.16 or am I doing something wrong?

    • krishna says:

      you have to run the soapcpp2 by eliminating the -i and -C options. Just by simply saying like this
      soapcpp2 myfile.h this will generate all the missing files that you are looking for. I think the reply is too late but still answering it because someone might be looking for this.

  3. Maricris Villareal says:

    Hi, FYI, the change needs to be made in the Proxy CPP, not header file.

  4. Ruben says:

    Thanks!
    You can add the parameter ‘-1’ to the soapcpp2 call, it will generate SOAP1.1 bindings; then you don’t have to change the proxy file by hand.

  5. GeorgeU says:

    When I run wsdl2h I get a ‘Floating point exception’.
    The URL of the service loads fine in browser. Ping from console to the hosting machine also works so it is not a proxy setting made only on browser.
    Can anyone help me?

    ./wsdl2h -t /usr/local/share/gsoap/WS/typemap.dat -o aaa.h “http://xxx:8087/yyyService.svc?wsdl”

    ** The gSOAP WSDL/Schema processor for C and C++, wsdl2h release 2.8.6
    ** Copyright (C) 2000-2011 Robert van Engelen, Genivia Inc.
    ** All Rights Reserved. This product is provided “as is”, without any warranty.
    ** The wsdl2h tool is released under one of the following two licenses:
    ** GPL or the commercial license by Genivia Inc. Use option -l for details.

    Saving aaa.h

    Reading type definitions from type map file ‘/usr/local/share/gsoap/WS/typemap.dat’

    Connecting to ‘http://xxx:8087/yyyService.svc?wsdl’ to retrieve WSDL/XSD…
    Floating point exception

    • krishna says:

      Can you tell me how the WSDL file that you mentioned above was generated. Did you use WCF (C#) web services or you have written it?

  6. I test in C++ ==> Run OK !!!!

    But I want to build Client use C programming ?

    struct soap soap; // the gSOAP runtime context
    struct _ns3__HelloWorld _ns3__HelloWorld;
    struct _ns3__HelloWorldResponse _ns3__HelloWorldResponse;
    memset( &_ns3__HelloWorld, 0, sizeof( struct _ns3__HelloWorld ));
    memset( &_ns3__HelloWorldResponse, 0, sizeof( struct _ns3__HelloWorldResponse ));
    soap_init(&soap); // initialize the context (only once!)

    _ns3__HelloWorld.msg = soap_strdup(&soap,”aaaaaac”);
    soap_set_namespaces( &soap, a_namespaces );// set Name Space
    if (soap_call___ns1__HelloWorld(&soap,”http://192.168.1.2/QuanLyNoCuoc”,NULL, &_ns3__HelloWorld, &_ns3__HelloWorldResponse) == SOAP_OK){
    printf(“kq=%s”,_ns3__HelloWorldResponse.HelloWorldResult);
    }
    else // an error occurred
    soap_print_fault(&soap, stderr); // display the SOAP fault message on the stderr stream*/

    I have error:
    Error 400 fault: SOAP-ENV:Server [no subcode]
    “HTTP Error”
    Detail: HTTP/1.1 400 Bad Request

    wsdl2h -c -o test.h http://192.168.1.2/QuanLyNoCuoc?wsdl
    soapcpp2 test.h

    I know that when I send by C program, header not same as header in WCF

    What mus I do ?

  7. Vidya says:

    Hi , i am trying to add security header to the client.WS-security.username token and Timestamp, have you got any idea on how to do it..

    Thank you

  8. rainer says:

    i have build basic (very basic) WCF client-server application, which works fine. when i try command
    .\wsdl2h.exe -o myheader.h “http://localhost:5000/service?wsdl” it reports an Error 400 fault: SOAP-ENV:Server [no subcode]. i have tried it on linux (compiled thru make) and winxp (used precompiled exe). whats wrong?

  9. saikiran says:

    That’s a good one.
    One thing I would like to ask you, have you ever test WCF wsHttpBinding in the linux. Becasue as far as I know gsoap won’t support the wsHttpBinding.

Leave a comment